/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package l1j.server.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;

import l1j.server.Config;
import l1j.server.L1Message;
import l1j.server.server.datatables.BossCycleTable;
import l1j.server.server.datatables.BrandRuneTable;
import l1j.server.server.datatables.CastleTable;
import l1j.server.server.datatables.CharacterTable;
import l1j.server.server.datatables.ClanSkillTable;
import l1j.server.server.datatables.ClanTable;
import l1j.server.server.datatables.DeleteItemPriceTable;
import l1j.server.server.datatables.DoorTable;
import l1j.server.server.datatables.DropTable;
import l1j.server.server.datatables.DropItemTable;
import l1j.server.server.datatables.EnchantSuccessChance;
import l1j.server.server.datatables.EnchantWeaponAdvance;
import l1j.server.server.datatables.FurnitureItemTable;
import l1j.server.server.datatables.FurnitureSpawnTable;
import l1j.server.server.datatables.GetBackRestartTable;
import l1j.server.server.datatables.IpTable;
import l1j.server.server.datatables.ItemTable;
import l1j.server.server.datatables.MagicDollTable;
import l1j.server.server.datatables.MailTable;
import l1j.server.server.datatables.MakeItemTable;
import l1j.server.server.datatables.MapDropTable;
import l1j.server.server.datatables.MapsTable;
import l1j.server.server.datatables.MobGroupTable;
import l1j.server.server.datatables.NpcActionTable;
import l1j.server.server.datatables.NpcChatTable;
import l1j.server.server.datatables.NpcTable;
import l1j.server.server.datatables.NPCTalkDataTable;
import l1j.server.server.datatables.PetTable;
import l1j.server.server.datatables.PetTypeTable;
import l1j.server.server.datatables.PolyTable;
import l1j.server.server.datatables.QuestTable;

import l1j.server.server.datatables.ResolventTable;
import l1j.server.server.datatables.ShopTable;
import l1j.server.server.datatables.SkillsTable;
import l1j.server.server.datatables.SprTable;
import l1j.server.server.datatables.UBSpawnTable;
import l1j.server.server.datatables.WeaponEffectTable;
import l1j.server.server.model.Dungeon;
import l1j.server.server.model.Getback;
import l1j.server.server.model.L1CastleLocation;
import l1j.server.server.model.L1NpcRegenerationTimer;
import l1j.server.server.model.L1Rank;
import l1j.server.server.model.L1World;

import l1j.server.server.model.clan.HouseDeadlineTimer;
import l1j.server.server.model.gametime.L1GameTimeClock;
import l1j.server.server.model.item.L1TreasureBox;
import l1j.server.server.model.map.L1WorldMap;
import l1j.server.server.model.npc.action.L1NpcDefaultAction;
import l1j.server.server.model.spawn.NpcSpawnTable;
import l1j.server.server.model.spawn.SpawnRefresher;
import l1j.server.server.model.spawn.SpawnTable;
import l1j.server.server.utils.SystemUtil;

// Referenced classes of package l1j.server.server:
// ClientThread, Logins, RateTable, IdFactory,
// LoginController, GameTimeController, Announcements,
// MobTable, SpawnTable, SkillsTable, PolyTable,
// TeleportLocations, ShopTable, NPCTalkDataTable, NpcSpawnTable,
// IpTable, Shutdown, NpcTable, MobGroupTable, NpcShoutTable

public class GameServer extends Thread {
	private ServerSocket _serverSocket;
	private static Logger _log = Logger.getLogger(GameServer.class.getName());
	private static int YesNoCount = 0;
	private int _port;

	@Override
	public void run() {
		System.out.println(L1Message.memoryUse + SystemUtil.getUsedMemoryMB()
				+ L1Message.memory);
		System.out.println(L1Message.waitingforuser);
		while (true) {
			try {
				Socket socket = _serverSocket.accept();
				System.out.println(L1Message.from + socket.getInetAddress()
						+ L1Message.attempt);
				String host = socket.getInetAddress().getHostAddress();
				if (IpTable.getInstance().isBannedIp(host)) {
					_log.info("banned IP(" + host + ")");
				} else {
					ClientThread client = new ClientThread(socket);
					GeneralThreadPool.getInstance().execute(client);
				}
			} catch (IOException ioexception) {
			}
		}
	}

	private static GameServer _instance = new GameServer();

	private GameServer() {
		super("GameServer");
	}

	public static GameServer getInstance() {
		return _instance;
	}

	public void initialize() throws Exception {
		String s = Config.GAME_SERVER_HOST_NAME;

		// Locale 多國語系
		L1Message.getInstance();

		_port = Config.GAME_SERVER_PORT;
		if (!"*".equals(s)) {
			InetAddress inetaddress = InetAddress.getByName(s);
			inetaddress.getHostAddress();
			_serverSocket = new ServerSocket(_port, 50, inetaddress);
			System.out.println(L1Message.setporton + _port);
		} else {
			_serverSocket = new ServerSocket(_port);
			System.out.println(L1Message.setporton + _port);
		}

		SystemUtil.printMemoryMessage("載入Server...");
		IdFactory.getInstance();
		SystemUtil.printMemoryMessage("載入IdFactory...");
		MapsTable.getInstance();
		L1WorldMap.getInstance();
		SystemUtil.printMemoryMessage("載入地圖系統...");
		ItemTable.getInstance();
		SystemUtil.printMemoryMessage("載入ItemTable...");
		// 讀取所有角色名稱
		CharacterTable.getInstance().loadAllCharName();

		// 初始化角色的上線狀態
		CharacterTable.clearOnlineStatus();
		SystemUtil.printMemoryMessage("載入CharacterTable...");
		// 初始化遊戲時間
		L1GameTimeClock.init();
		SystemUtil.printMemoryMessage("載入L1GameTimeClock...");
		EnchantWeaponAdvance.initilize();
		SystemUtil.printMemoryMessage("載入EnchantWeaponAdvance...");
		EnchantSuccessChance.initilize();
		SystemUtil.printMemoryMessage("載入EnchantSuccessChance...");
		// 初始化無限大戰
		UbTimeController ubTimeContoroller = UbTimeController.getInstance();
		GeneralThreadPool.getInstance().execute(ubTimeContoroller);
		SystemUtil.printMemoryMessage("載入UbTimeController...");
		// 初始化攻城
		WarTimeController warTimeController = WarTimeController.getInstance();
		GeneralThreadPool.getInstance().execute(warTimeController);
		SystemUtil.printMemoryMessage("載入WarTimeController...");
		// 設定精靈石的產生
		// if (Config.ELEMENTAL_STONE_AMOUNT > 0) {
		// ElementalStoneGenerator elementalStoneGenerator =
		// ElementalStoneGenerator
		// .getInstance();
		// GeneralThreadPool.getInstance().execute(elementalStoneGenerator);
		// }

		// 初始化 HomeTown 時間
		// HomeTownTimeController.getInstance();
		// System.out.println("HomeTownTimeController..." + "loaded"
		// + L1Message.memoryUse + SystemUtil.getUsedMemoryMB()
		// + L1Message.memory);
		// 初始化盟屋拍賣

		HouseDeadlineTimer.initilize();
		SystemUtil.printMemoryMessage("載入盟屋系統...");
		// 初始化釣魚
		FishingTimeController fishingTimeController = FishingTimeController
				.getInstance();
		GeneralThreadPool.getInstance().execute(fishingTimeController);
		SystemUtil.printMemoryMessage("載入FishingTimeController...");
		// 初始化 NPC 聊天
		NpcChatTimeController npcChatTimeController = NpcChatTimeController
				.getInstance();
		GeneralThreadPool.getInstance().execute(npcChatTimeController);
		SystemUtil.printMemoryMessage("載入NpcChatTimeController...");
		// 初始化 Light
		// LightTimeController lightTimeController = LightTimeController
		// .getInstance();
		// GeneralThreadPool.getInstance().execute(lightTimeController);
		// SystemUtil.printMemoryMessage("載入LightTimeController...");
		// 初始化遊戲公告
		Announcements.getInstance();

		// 初始化遊戲循環公告
		AnnouncementsCycle.getInstance();
		SystemUtil.printMemoryMessage("載入Announcements...");
		// 初始化MySQL自動備份程序
		// MysqlAutoBackup.getInstance();

		// 開始 MySQL自動備份程序 計時器
		// MysqlAutoBackupTimer.TimerStart();

		NpcTable.getInstance().initilize();
		// L1DeleteItemOnGround deleteitem = new L1DeleteItemOnGround();
		// deleteitem.initialize();
		SystemUtil.printMemoryMessage("載入NpcTable...");
		if (!NpcTable.getInstance().isInitialized()) {
			throw new Exception("Could not initialize the npc table");
		}
		L1NpcDefaultAction.getInstance();
		SystemUtil.printMemoryMessage("載入L1NpcDefaultAction...");
		DoorTable.initialize();
		SystemUtil.printMemoryMessage("載入DoorTable...");
		SpawnTable.getInstance();
		SystemUtil.printMemoryMessage("載入SpawnTable...");
		MobGroupTable.getInstance();
		SystemUtil.printMemoryMessage("載入MobGroupTable...");
		SkillsTable.getInstance();
		SystemUtil.printMemoryMessage("載入SkillsTable...");
		PolyTable.getInstance();
		SystemUtil.printMemoryMessage("載入PolyTable...");
		DropTable.getInstance();
		SystemUtil.printMemoryMessage("載入DropTable...");
		DropItemTable.getInstance();
		SystemUtil.printMemoryMessage("載入DropItemTable...");
		ShopTable.getInstance();
		SystemUtil.printMemoryMessage("載入ShopTable...");
		NPCTalkDataTable.getInstance();
		SystemUtil.printMemoryMessage("載入NPCTalkDataTable...");
		L1World.getInstance();
		SystemUtil.printMemoryMessage("載入L1World...");
		// L1WorldTraps.getInstance();
		// SystemUtil.printMemoryMessage("載入L1WorldTraps...");
		Dungeon.getInstance();
		SystemUtil.printMemoryMessage("載入Dungeon...");
		NpcSpawnTable.getInstance();
		SystemUtil.printMemoryMessage("載入NpcSpawnTable...");
		IpTable.getInstance();
		SystemUtil.printMemoryMessage("載入IpTable...");

		UBSpawnTable.getInstance();
		SystemUtil.printMemoryMessage("載入UBSpawnTable...");
		PetTable.getInstance();
		SystemUtil.printMemoryMessage("載入PetTable...");
		ClanTable.getInstance();
		SystemUtil.printMemoryMessage("載入ClanTable...");
		CastleTable.getInstance();
		SystemUtil.printMemoryMessage("載入CastleTable...");
		L1CastleLocation.setCastleTaxRate(); // 必須在 CastleTable 初始化之後
		SystemUtil.printMemoryMessage("載入L1CastleLocation...");
		GetBackRestartTable.getInstance();
		SystemUtil.printMemoryMessage("載入GetBackRestartTable...");
		L1NpcRegenerationTimer.getInstance();
		SystemUtil.printMemoryMessage("載入L1NpcRegenerationTimer...");
		NpcActionTable.load();
		SystemUtil.printMemoryMessage("載入NpcActionTable...");
		GMCommandsConfig.load();
		SystemUtil.printMemoryMessage("載入GMCommandsConfig...");
		Getback.loadGetBack();
		SystemUtil.printMemoryMessage("載入Getback...");
		PetTypeTable.load();
		SystemUtil.printMemoryMessage("載入PetTypeTable...");
		L1TreasureBox.load();
		SystemUtil.printMemoryMessage("載入L1TreasureBox...");
		SprTable.getInstance();
		SystemUtil.printMemoryMessage("載入SprTable...");
		ResolventTable.getInstance();
		SystemUtil.printMemoryMessage("載入ResolventTable...");
		FurnitureSpawnTable.getInstance();
		SystemUtil.printMemoryMessage("載入FurnitureSpawnTable...");
		NpcChatTable.getInstance();
		SystemUtil.printMemoryMessage("載入NpcChatTable...");
		MailTable.getInstance();
		SystemUtil.printMemoryMessage("載入MailTable...");
		MagicDollTable.getInstance();
		SystemUtil.printMemoryMessage("載入MagicDollTable...");
		FurnitureItemTable.getInstance();
		SystemUtil.printMemoryMessage("載入FurnitureItemTable...");
		WeaponEffectTable.initilize();
		SystemUtil.printMemoryMessage("載入WeaponEffectTable...");
		MakeItemTable.initilize();
		SystemUtil.printMemoryMessage("載入MakeItemTable...");
		BossCycleTable.initilize();
		SystemUtil.printMemoryMessage("載入BossCycleTable...");
		SpawnRefresher.initilize();
		SystemUtil.printMemoryMessage("載入SpawnRefresher...");
		MapDropTable.initilize();
		SystemUtil.printMemoryMessage("載入MapDropTable...");
		DeleteItemPriceTable.initilize();
		SystemUtil.printMemoryMessage("載入DeleteItemPriceTable...");
		QuestTable.initilize();
		SystemUtil.printMemoryMessage("載入QuestTable...");
		ClanSkillTable.initilize();
		SystemUtil.printMemoryMessage("載入ClanSkillTable...");
		BrandRuneTable.initilize();
		SystemUtil.printMemoryMessage("載入BrandRuneTable...");
		ShutdownTimer.initilize();
		SystemUtil.printMemoryMessage("載入ShutdownTimer...");
		L1Rank.getInstance();
        SystemUtil.printMemoryMessage("載入L1Rank...");
		System.out.println(L1Message.initialfinished);
		Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
		this.start();
	}

	private class ServerShutdownThread extends Thread {
		private final int _secondsCount;

		public ServerShutdownThread(int secondsCount) {
			_secondsCount = secondsCount;
		}

		@Override
		public void run() {
			L1World world = L1World.getInstance();
			try {
				int secondsCount = _secondsCount;
				world.broadcastServerMessage("伺服器即將關閉。");
				world.broadcastServerMessage("請玩家移動到安全區域先行登出");
				while (0 < secondsCount) {
					if (secondsCount <= 30) {
						world.broadcastServerMessage("伺服器將在" + secondsCount
								+ "秒後關閉，請玩家移動到安全區域先行登出。");
					} else {
						if (secondsCount % 60 == 0) {
							world.broadcastServerMessage("伺服器將在" + secondsCount
									/ 60 + "分鐘後關閉。");
						}
					}
					Thread.sleep(1000);
					secondsCount--;
				}
				shutdown();
			} catch (InterruptedException e) {
				world.broadcastServerMessage("已取消伺服器關機。伺服器將會正常運作。");
				return;
			}
		}
	}

	private ServerShutdownThread _shutdownThread = null;

	public synchronized void shutdownWithCountdown(int secondsCount) {
		if (_shutdownThread != null) {
			// 如果正在關閉
			// TODO 可能要有錯誤通知之類的
			return;
		}
		_shutdownThread = new ServerShutdownThread(secondsCount);
		GeneralThreadPool.getInstance().execute(_shutdownThread);
	}

	public void shutdown() {
		System.exit(0);
	}

	public synchronized void abortShutdown() {
		if (_shutdownThread == null) {
			// 如果正在關閉
			// TODO 可能要有錯誤通知之類的
			return;
		}

		_shutdownThread.interrupt();
		_shutdownThread = null;
	}

	/**
	 * 取得世界中發送YesNo總次數
	 * 
	 * @return YesNo總次數
	 */
	public static int getYesNoCount() {
		YesNoCount += 1;
		return YesNoCount;
	}
}
